package com.julius.design.thread.lock;

import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author julius zhou
 * @date 2023/3/3 10:02
 * <p>
 *      实现多个线程交替打印ABC，多种方式<br/>
 *
 *     <li>
 *         <ol>join</ol>
 *         <ol>ReentrantLock</ol>
 *         <ol>Lock+Condition 精确唤醒指定线程，避免空轮询</ol>
 *         <ol>Semaphore 信号量</ol>
 *     </li>
 * </p>
 **/
public class PollPrintTest {


    private static int num;

    private static Lock reentrantLock = new ReentrantLock();

    /**
     * 三个锁等待条件
     */

    private static Condition conditionA = reentrantLock.newCondition();

    private static Condition conditionB = reentrantLock.newCondition();

    private static Condition conditionC = reentrantLock.newCondition();


    /**
     * 定义三个信号量
     */
    private static Semaphore semaphoreA = new Semaphore(1);

    private static Semaphore semaphoreB = new Semaphore(0);

    private static Semaphore semaphoreC = new Semaphore(0);







    /**
     * 真是执行次数
     */
    private static AtomicInteger total = new AtomicInteger(0);

    /**
     * 使用join交替打印ABC,实验下来多核cpu是没有办法保证的
     */
    public static void joinPrint() {
        Thread threadA = new Thread(()->{
            System.out.println('A');
        });

        Thread threadB = new Thread(()->{
            System.out.println('B');
            try {
                threadA.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread threadC = new Thread(()->{
            System.out.println('C');
            try {
                threadB.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        threadA.start();
        threadB.start();
        threadC.start();
    }

    /**
     * 使用Lock加上判断可以实现交替打印，但是这样容易出现cpu消耗过高,空轮询次数过高，不适合
     * @param target
     */
    public static void locPrint(int target) {
        for (int i = 0; i < 10;) {
            total.incrementAndGet();
            reentrantLock.lock();
            if (num % 3 == target) {
                System.out.println(Thread.currentThread().getName());
                num++;
                i++;
            }
            System.out.println("目前轮询次数："+total.get());
            reentrantLock.unlock();
        }
    }




    /**
     * 使用Lock+Condition精准唤醒线程，避免空轮询消耗cpu,满足交替打印的基本要求
     * @param target 阻塞条件
     * @param current 当前条件
     * @param next 需要唤醒的条件
     */
    public static void conditionPrint(int target,Condition current,Condition next) {

        for (int i = 0; i < 10;) {
            reentrantLock.lock();
            total.incrementAndGet();
            try {
                //阻塞判断，这里线程不满足的时候会阻塞在这里
                while (num % 3 != target) {
                    current.await();
                }
                num++;
                i++;
                System.out.println(Thread.currentThread().getName());
                System.out.println("轮询次数："+total.get());
                next.signal();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                reentrantLock.unlock();
            }
        }



    }


    /**
     * 使用Semaphore进行交替打印，交替获取锁
     * @param current 当前信号量
     * @param next 下一个信号量
     */
    public static void semaphorePrint(Semaphore current,Semaphore next) {

        for (int i = 0; i < 10; i++) {
            try {
                //使用上一个信号量
                current.acquire();
                total.incrementAndGet();
                System.out.println("轮询次数："+total.get()+"，当前线程："+Thread.currentThread().getName());
                System.out.println();
                //释放下一个信号量
                next.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
//        for (int i = 0; i < 3; i++) {
//            joinPrint();
//        }
//        new Thread(()->locPrint(0),"A").start();
//        new Thread(()->locPrint(1),"B").start();
//        new Thread(()->locPrint(2),"C").start();


//        new Thread(()->conditionPrint(0,conditionA,conditionB),"A").start();
//        new Thread(()->conditionPrint(1,conditionB,conditionC),"B").start();
//        new Thread(()->conditionPrint(2,conditionC,conditionA),"C").start();

        new Thread(()->semaphorePrint(semaphoreA,semaphoreB),"A").start();
        new Thread(()->semaphorePrint(semaphoreB,semaphoreC),"B").start();
        new Thread(()->semaphorePrint(semaphoreC,semaphoreA),"C").start();




    }
}
